import ts from 'typescript';
import fs from 'fs';
import path from 'path';
import { logInfo, logWarn, logError } from './logger';

// 以下变量用于管理 tsconfig 文件的路径和备份状态
let tempTsconfigPath = ''; // 自动生成的 tsconfig.json 路径
let userTsconfigPath = ''; // 用户原始 tsconfig.json 路径
let backupTsconfigPath = ''; // 用户 tsconfig 的备份路径
let hasUserTsconfig = false; // 是否存在用户 tsconfig
let isCleaned = false;

/**
 * 清理工作：
 * - 删除自动生成的 tsconfig.json
 * - 恢复用户原始 tsconfig.json
 * - 恢复同级目录的原始 tsconfig.json（如果被覆盖）
 */
function cleanup() {
    if (isCleaned) {
        return;
    }
    isCleaned = true;
    if (fs.existsSync(tempTsconfigPath)) {
        fs.unlinkSync(tempTsconfigPath);
        logInfo('已删除自动生成的 tsconfig.json');
    }
    if (hasUserTsconfig && fs.existsSync(backupTsconfigPath)) {
        fs.renameSync(backupTsconfigPath, userTsconfigPath);
        logInfo('已还原项目原 tsconfig.json');
    }
}

// 注册退出或中断信号处理，保证无论什么情况都能清理干净
process.on('SIGINT', () => {
    logWarn('接收到中断信号，正在清理...');
    cleanup();
    process.exit(1);
});

process.on('exit', () => {
    cleanup();
});

/**
 * 生成类型声明文件（.d.ts）
 * @param inputPath 输入的文件或目录路径
 * @param isFile 是否为单文件模式
 */
export async function generateTypes(inputPath: string, isFile: boolean = false): Promise<void> {
    const inputDir = isFile ? path.dirname(inputPath) : inputPath;
    console.log('input', inputDir);

    const outDir = path.join(inputDir, isFile ? './types' : '../types'); // 输出目录
    // 检查types目录是否存在
    if (fs.existsSync(outDir)) {
        fs.rmSync(outDir, { recursive: true, force: true });
        logWarn('已删除原有types文件夹');
    }

    // 检查输入路径是否存在
    if (!fs.existsSync(inputPath)) {
        throw new Error(`路径不存在: ${inputPath}`);
    }

    // 收集要编译的文件列表
    const fileList = isFile ? [inputPath] : collectFiles(inputDir, ['.ts', '.js'], outDir);
    if (fileList.length === 0) {
        logWarn('未找到任何 .ts 或 .js 文件');
        return;
    }

    // 配置 tsconfig 路径
    userTsconfigPath = path.join(inputDir, isFile ? './tsconfig.json' : '../tsconfig.json'); // 同级目录 tsconfig
    hasUserTsconfig = fs.existsSync(userTsconfigPath);
    backupTsconfigPath = hasUserTsconfig ? path.join(inputDir, isFile ? './tsconfig.backup.json' : '../tsconfig.backup.json') : '';
    tempTsconfigPath = path.join(inputDir, isFile ? './tsconfig.json' : '../tsconfig.json'); // 同级目录 tsconfig

    // 如果有原始 tsconfig，备份它
    if (hasUserTsconfig) {
        fs.copyFileSync(userTsconfigPath, backupTsconfigPath);
        logWarn('检测到项目中存在 tsconfig.json，已备份为 tsconfig.backup.json');
    }

    // 构建用于生成声明文件的 tsconfig 配置
    const compilerOptions: ts.CompilerOptions = {
        allowJs: true,
        strict: false,
        declaration: true,
        emitDeclarationOnly: true,
        target: ts.ScriptTarget.ES2020,
        module: ts.ModuleKind.ESNext,
        moduleResolution: ts.ModuleResolutionKind.Node10,
        skipLibCheck: true,
        esModuleInterop: true,
        resolveJsonModule: true,
        allowSyntheticDefaultImports: true,
        sourceMap: true,
        jsx: ts.JsxEmit.Preserve,
        rootDir: inputDir,
        outDir
    };

    // 写入自动生成的 tsconfig.json
    const tsconfigContent = JSON.stringify({ compilerOptions, include: ['.'] }, null, 2);
    fs.writeFileSync(tempTsconfigPath, tsconfigContent);

    try {
        // 创建编译器主机与 Program
        const host = ts.createCompilerHost(compilerOptions);
        const program = ts.createProgram(fileList, compilerOptions, host);
        const emitResult = program.emit();

        // 收集所有编译诊断信息
        const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
        allDiagnostics.forEach((diagnostic) => {
            const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
            if (diagnostic.file) {
                const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
                const fileName = diagnostic.file.fileName;
                logError(`${fileName} (${line + 1},${character + 1}): ${message}`);
            } else {
                logError(message);
            }
        });

        // 如果声明文件生成失败，抛出错误
        if (emitResult.emitSkipped) {
            throw new Error('声明文件生成失败，请查看上方错误信息。');
        }
        logInfo('声明文件生成完成');
    } catch (err) {
        logError(`生成过程中发生错误: ${(err as Error).message}`);
        throw err;
    } finally {
        // 最终执行清理操作（成功或异常都会执行）
        cleanup();
    }
}

/**
 * 递归收集指定目录下的 .ts / .js 文件，排除输出目录
 * @param dir 要扫描的目录
 * @param exts 要匹配的扩展名
 * @param excludeDir 要排除的目录（通常为 outDir）
 * @param collected 收集到的文件数组（递归用）
 * @returns 匹配的文件路径数组
 */
function collectFiles(dir: string, exts: string[], excludeDir: string, collected: string[] = []): string[] {
    const items = fs.readdirSync(dir);
    for (const item of items) {
        const fullPath = path.join(dir, item);
        const stat = fs.statSync(fullPath);
        if (fullPath.startsWith(excludeDir)) continue;
        if (stat.isDirectory()) {
            collectFiles(fullPath, exts, excludeDir, collected);
        } else if (exts.includes(path.extname(item))) {
            collected.push(fullPath);
        }
    }
    return collected;
}
